#pragma once           

#include <sys/epoll.h>
#include <unistd.h>
#include "Socket.hpp"
#include "InetAddr.hpp"

class EpollServer
{
    const int time =  5 * 10 * 10 * 10;
    const static int num = 1024;
public:    
    EpollServer(uint16_t port) : _port(port)
    {  
        // 创建TCP套接字
        _listensock = TcpSocket::server::Socket();
        TcpSocket::server::Bind(_listensock, _port);
        TcpSocket::server::Listen(_listensock);

        // 创建epoll模型
        _epfd = epoll_create(128);
        if (_epfd < 0)
        {
            perror("epoll_create error");
            exit(-1);
        }
        // 注意：监听套接字关心的事件是读事件, 当客户端连接发送过来时，它开始读数据进行三次握手，即连接到来时它对应的事件才准备就绪
        struct epoll_event ev;
        ev.events = EPOLLIN; 
        ev.data.fd = _listensock; 
        epoll_ctl(_epfd, EPOLL_CTL_ADD, _listensock, &ev);
    }
    
    // select服务器运行
    void Run()
    {    
        int timeout = time;
        while (true)
        {
            int n = epoll_wait(_epfd, _rev, num, timeout);
            switch (n)
            {
            case 0:
                cout << "epoll_wait timeout" << endl;
                break;
            case -1:
                cout << "epoll_wait error" << endl;
                break;
            default:
                Soluation(n);
                cout << "....................事件已处理...................." << endl;
                break;
            }
        }
    }
    
    // 处理已就绪的读事件
    void Soluation(int n)
    {
        for (int i = 0; i < n; i++)
        {
            // 从数组_rev中拿到就绪事件和文件描述符
            uint32_t event = _rev[i].events; // 要处理的事件
            int sockfd = _rev[i].data.fd;    // 要处理事件的文件描述符

            // 服务器一般主要处理读事件，所有这里只处理读事件
            if (event & EPOLLIN)
            {
                // 监听文件描述符就绪，即远端在进行三次握手建立连接
                if (sockfd == _listensock)
                {
                    // 获取客户端连接
                    struct sockaddr_in peer;
                    socklen_t len = sizeof(peer);
                    int clientfd = accept(_listensock, (struct sockaddr*)&peer, &len); 
                    cout << "accept successful" << endl;
                    InetAddr clientaddr(peer);
                    printf("Get a new Link, sockfd: %d, client addr %s:%d\n", clientfd, clientaddr.Ip().c_str(), clientaddr.Port());

                    // 获取连接后，开始监听事件
                    struct epoll_event ev;
                    ev.events = EPOLLIN;
                    ev.data.fd = clientfd;
                    epoll_ctl(_epfd, EPOLL_CTL_ADD, clientfd, &ev);
                }
                // 远端的IO事件已就绪
                else
                {
                    char buffer[1024];
                    int n = recv(sockfd, &buffer, sizeof(buffer) - 1, 0);
                    switch (n)
                    {
                    // 读取失败
                    case -1:
                        cout << sockfd << " :recv error" << endl;
                        epoll_ctl(_epfd, EPOLL_CTL_DEL, sockfd, nullptr); // 切记不要忘了从epoll结构中删除出现问题的句柄，且要先epoll_ctl删除，再close关闭
                        close(sockfd);
                        break;
                    // 对端关闭连接
                    case 0:
                        cout << sockfd << " :is closed" << endl;
                        epoll_ctl(_epfd, EPOLL_CTL_DEL, sockfd, nullptr); // 切记不要忘了从epoll结构中删除出现问题的句柄，且要先epoll_ctl删除，再close关闭
                        close(sockfd);
                        break;
                    // 正常接收对端信息
                    default:
                        buffer[n] = '\0';
                        printf("[client: %d]# %s", sockfd, buffer);
                        string str = "[selectserver]: 数据已收到\n";
                        send(sockfd, str.c_str(), sizeof(str), 0);
                        break;
                    }
                }
            }
        }        
    }

    //释放连接
    ~EpollServer()
    {
        close(_listensock);
    }
private:
    int _listensock; // 监听套接字    
    uint16_t _port;  // 端口号  
    int _epfd;  // epoll实例的文件描述符
    struct epoll_event _rev[num];  // 已准备就绪的文件描述符
};

